/* $Id: ld.js 40 2011-08-01 11:13:06Z agnes.haasser $ */

var plateau_jeu;
var settings;
var wiz;
var monsters = new Array();
var directions = new Array(1,2,3,4,5,6);
var score=0;
function randomFromTo(from, to){
       return Math.floor(Math.random() * (to - from + 1) + from);
}

function factorial(n) {
	if(n <= 0) {
		return 1;
	} else {
		return n * factorial( n-1 );
	}
}

function shuffle(o){ //v1.0
	for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
	return o;
};

Array.prototype.unique=function(){
	var i=0, mx=this.length, last, o=[], r=this.concat().sort();
	for(i;i<mx;i++){ if(last!==r[i]){last=o[o.length]=r[i];} }
	return o;
}
/*
 * CSS classes of hex_s elements contain information
 * about their line and column. This function returns
 * an array containing the line and column corresponding to
 * the CSS class passed in parameter.
 */
function getLineAndColFromCssClass(classes) {
	// a "l" followed by a "-" and 1+ digits.
	// capture the digits.
	var lineRegexp = /l\-([0-9]+)/;
	var lines = lineRegexp.exec(classes);
	// a "c" followed by a "-" and 1+ digits.
	// capture the digits.
	var colRegexp = /c\-([0-9]+)/;
	var cols = colRegexp.exec(classes);
	if(!cols || !lines) {
		return false;
	} else {
		return Array(lines[1], cols[1]);
		// lines[0] is the whole expression matched by lineRegexp,
		// lines[1] is the text captured by first capturing parenthesis.
	}
	
}

/*
 * Generates an array containing terrain types,
 * and returns a function allowing later tests
 * on terrain type.
 * May be modified to allow terrain modifications.
 */
function terrainGenerator( width, height ) {
	var i,j,curLine,previousI,curCol,penultimateI,previousJ,
            counter,generator,topSea,bottomSea,
            terrain,fieldcounter = 0;
	// Create a width*height 2D array of zeros.
	// (no better way to do it ?)
        // D. Crockford says no. :'(
	terrain = new Array();
	for ( curLine = 0; curLine < height; curLine++) {
		terrain[curLine] = new Array();
		for ( curCol = 0; curCol < width; curCol++) {
			terrain[curLine][curCol] = 0;
		}
	}
	// Fill terrain array with some fields...
	// Type 1 : rock - pick some random places and stop at one moment
	while(Math.random() < 0.9 || fieldcounter < width*height/10) {
		curLine = randomFromTo(0, height-1);
		curCol = randomFromTo(0, width - 1);
		if( terrain[curLine][curCol] == 0 ) {
			fieldcounter = fieldcounter + 1;
			terrain[curLine][curCol] = 1;
		}
	}
	fieldcounter = 0;
	// Type 5 : forest - try to do some groups of random dimensions
	while(Math.random() < 0.5 || fieldcounter < width*height/10) {
		curLine = randomFromTo(3, height - 4);
		curCol = randomFromTo(3, width - 4);
		if( terrain[curLine][curCol] == 0 ) {
			var toFill = new Array(	randomFromTo( curLine - 3, curLine),
								randomFromTo( curLine, curLine + 3),
								randomFromTo( curCol - 3, curCol),
								randomFromTo( curCol, curCol + 3)
						);
			for( i = toFill[0]; i <= toFill[1]; i++ ) {
				for( j = toFill[2]; j <= toFill[3]; j++) {
					terrain[i][j] = (terrain[i][j] == 0) ? 5 : terrain[i][j] ;
				}
			}
			fieldcounter = fieldcounter + ( toFill[1] - toFill[0]) * ( toFill[3] - toFill[2] );
		}
	}
	// Type 3 : quiet water - sort of "river". Overrides forest but not rock (type 1).
	i = randomFromTo( 3 , height - 2 );
	j = 0 ;
	previousI = 0;
	while( j < height ) {
		penultimateI = previousI;
		// I've always dreamed to use "penultimate" in a program.
		// I use this variable to make beautiful meanders.
		previousI = i;
		previousJ = j;
		i = randomFromTo( previousI-1 , previousI+1 );
		i = Math.min( height - 1, i );
		i = Math.max( 0, i );
		counter = 0;
		while( (terrain[i][j] == 1 || i == penultimateI) && counter < 10 ) {
			i = randomFromTo( previousI-1 , previousI+1 );
			i = Math.min( height - 1, i );
			i = Math.max( 0, i );
			counter = counter + 1;
		}
		if ( i % 2 == 1 ) {
			// change column only if we do not change line
			j = ( i == previousI ) ? j + 1 : j;
			j = ( j == 0 ) ? 1 : j;
		} else {
			// always change column
			j = j + 1 ;
		}
		terrain[i][j] = 3;
	}
	// Type 2 : sea - only on the border, where river has arrived.
	topSea = Math.max( 0, randomFromTo( i - 5, i - 2 ) );
	bottomSea = Math.min( height, randomFromTo( i + 2, i + 5) );
	for(i = topSea; i < bottomSea; i++) {
			terrain[i][width-1 - i%2] = 2;
			// place sand on the left of sea water if there is no rock or water
			terrain[i][width-2 - i%2] = (terrain[i][width-2 - i%2] == 1 || terrain[i][width-2 - i%2] == 3 ) ? terrain[i][width-2 - i%2] : 4;
	}
	// Type 4 : sand (near the sea)
	// Return generator function.
	generator = {
		getTerrain : function( abs, ord ) {
			return terrain[ord][abs];
		},
		setTerrain : function(abs,ord,value) {
			terrain[ord][abs] = value;
		}
	}
	return generator;
}

/*
 * Generates the memory for resource farming
 */
function generateMemory( width, height, plateau, from, to) {
	if(from==undefined) {
		from = 0;
	}
	if(to == undefined) {
		to = 5;
	}
	
	var values = new Array('2', 'Z', '8', '4', 'U', 'E', 'A', 'z');
    var myMemory = new Array();
	var theResources = new Array();
	var exploring = true;
	var foundResource;
	var numberOfFoundResources = 1;
    var curLine,curCol;
    for ( curLine = 0; curLine < height; curLine++) {
		myMemory[curLine] = new Array();
		for ( curCol = 0; curCol < width; curCol++) {
			myMemory[curLine][curCol] = randomFromTo(from, to - 1);
		}
	}
	for ( i = from; i <= to ; i++) {
		theResources[i] = 0;
	}
	
	return {
		getNumber : function(x,y) {
			return myMemory[x][y];
		},
		setNumber : function(x,y,value) {
			myMemory[x][y] = value;
		},
		getResourceValue : function(i) {
			return values[i];
		},
		viewResource : function() {
			if(exploring === true) {
				var classes = $(this).attr('class');
				var coords = getLineAndColFromCssClass(classes);
				$(this).html(values[myMemory[coords[0]][coords[1]]]);
			}
		},
		takeMana : function(manaType) {
			if(!theResources[manaType]) {
				return false;
			} else {
				theResources[manaType] = theResources[manaType] - 1;
				$('#count-' + manaType + ' span').html(theResources[manaType]);
				return true;
			}
		},
		hideResource : function() {
			$(this).html('&nbsp;');
		},
		clickResource : function() {
			if($(this).hasClass('clicked')
				|| $(this).hasClass('wizard')
				|| $(this).hasClass('minion')){
				return false;
			}
			var classes = $(this).attr('class');
			var coords = getLineAndColFromCssClass(classes);
			var newFoundResource = myMemory[coords[0]][coords[1]];
			myMemory[coords[0]][coords[1]] = randomFromTo(from, to - 1);
			$(this).addClass('clicked');
			
			if( exploring === true ) {
				exploring = false;
				plateau.toggleClass('exploring', exploring);
				foundResource = newFoundResource;
				numberOfFoundResources = 1;
				$('#hits-container').html('').show();
				$('#hits-container').append('<b>' + values[newFoundResource] + '</b><br />');
			} else {
				$('#hits-container').show();
				$('#hits-container').append('<b>' + values[newFoundResource] + '</b><br />');
				if( newFoundResource == foundResource) {
					numberOfFoundResources = numberOfFoundResources + 1;
				}
				if( numberOfFoundResources == 5 || newFoundResource != foundResource ) {
					exploring = true;
					plateau.toggleClass('exploring', exploring);
					theResources[foundResource] = theResources[foundResource] + factorial(numberOfFoundResources) ;
					$('#count-' + foundResource + ' span').html(theResources[foundResource]);
					changeScore(theResources[foundResource]);
					$('#hits-container').fadeOut(500);
				}
			}
			return false;
		}
	};
}

/*
 * Generates the object used for Spell table
 */
function generateSpellTable(resources) {
	var currentSpell = new Array();
	return {
		addMana : function(type) {
			var mana = resources.takeMana(type);
			if(mana === true) {
				var newLength = currentSpell.push(type);
			} else {
				var options = { color: "#990000"};
				$("#count-"+type).effect( "highlight", options, "fast", null );
				return false;
			}
			var newB = $('<b>' + resources.getResourceValue(type) + '</b>');
			if(newLength == 1) { //new spell
				var newLine = $('<li></li>');
				newLine.append(newB);
				$('#spells').prepend(newLine);
			} else {
				$('#spells li:first-child').append(newB);
			}
			if(newLength == 3) {
				var tmpSpell = currentSpell;
				changeScore(tmpSpell.unique().length*3);
				wiz.castSpell(currentSpell);
				currentSpell = new Array();
			}
			if($('#spells li').length > 6) {
				$('#spells li:last-child').fadeOut(400, function(){$('#spells li:last-child').remove()});
			}
		}
	};
}

function regenerateTerrain() {
	var step = function() {
		var myArray = $('.clicked');
		if(myArray.length) {
			var position = randomFromTo(0, myArray.length);
			$(myArray[position]).removeClass('clicked');
		}
		setTimeout(step, 8000);
	}
	setTimeout(step,20000);
}
function moveMonsters() {
    var monster_id;
	for( monster_id = 0; monster_id < monsters.length; monster_id++ ) {
            var theMonster = monsters[monster_id];
            theMonster.moveInDirection(randomFromTo(1,6));
	}	
}

function changeScore(delta) {
	score+= delta;
	$("#score").html(score);
}
function Sprite(type) {
	var _x=0;
	var _y=0;
	var spriteClass='';
	var spriteID='';
	var spriteType=type;
	switch(type) {
		case "wizard":
			this.spriteID="wizard";
			break;
		case "monster":
			this.spriteClass="monster";
			break;
		default:
			break;
			
			
	}
	var spriteTile;
	this.addToMap = function() {
			this.spriteTile = $("<div></div>");
			this.spriteTile.attr("id",this.spriteID);
			this.spriteTile.addClass(this.spriteClass);
                        if(type == "monster") {
                            this.spriteTile.addClass('m-' + randomFromTo(1,5));
                        }
			plateau.append(this.spriteTile);
		}
    this.testTarget = function(x,y) {
		var spriteMovable=false;

		if ((-1<y)&&(y<(settings.dimensions_plat_larg))) {
			var delta_x=0;
			if (y % 2 == 1) {
				delta_x = -1;
			}
			if ((-1<x)&&(x<(settings.dimensions_plat_haut+delta_x))) {
				spriteMovable=true;
			} else {
				spriteMovable=false;
			}
			delta_y=null;
		} else {
			spriteMovable=false;
		}
		if (spriteMovable) {
			switch(parseInt(plateau_jeu.terrain.getTerrain(x,y))) {
				//Terrain ID for "rocks"
				case 1:
					return false;
				//For all other types, the sprite is able to move
				default:
					return true;
			}
		} else {
			return false;
		}
	}
	this.moveTo = function(x,y) {
			if ((-1<x<settings.dimensions_plat_larg)&&(-1<y<settings.dimensions_plat_haut)) {
				//Ligne paire : 0,2,4 etc
				if (y % 2 == 0) {
					 this.spriteTile.animate({
						left: parseInt(plateau.css('paddingLeft'))+x*settings.dimensions_hex_larg,
						top: parseInt(plateau.css('paddingTop'))+0.75*(y*settings.dimensions_hex_haut)
					  }, 300, function() {
						// Animation complete.
					  });
				} else {
					this.spriteTile.animate({
						left: parseInt(plateau.css('paddingLeft'))+(x+0.5)*settings.dimensions_hex_larg,
						top: parseInt(plateau.css('paddingTop'))+0.75*(y*settings.dimensions_hex_haut)
					  }, 300, function() {
						// Animation complete.
					  });
				}
				_x = x;
				_y = y;
                                if(type == "monster") {
                                    $('.c-' + _x + '.l-' + _y).addClass('clicked');
                                }
			}
		}
        this.getTargetInDirection = function(direction, fromX, fromY) {
            if(fromX == undefined) {
                fromX = _x ;
            }
            if(fromY == undefined) {
                fromY = _y ;
            }
            var xTarg,yTarg;
            switch(direction) {
                case 1: // NW
                    // change columns (-1) only if we are on a odd line
                    xTarg = _x - (1 - _y%2);
                    yTarg = _y - 1;
                    break;
                case 2: // NE
                    // change columns (+1) only if we are on an even line
                    xTarg = _x + _y%2;
                    yTarg = _y - 1;
                    break;
                case 3: // E
                    xTarg = _x + 1;
                    yTarg = _y;
                    break;
                case 4: // SE
                    // change columns (+1) only if we are on a odd line
                    xTarg = _x + _y%2;
                    yTarg = _y + 1;
                    break;
                case 5: // SW
                    // change columns (-1) only if we are on an even line
                    xTarg = _x - (1 - _y%2);
                    yTarg = _y + 1;
                    break;
                case 6: // W
                    xTarg = _x - 1;
                    yTarg = _y;
                    break;
                default:
                    return false;
                    break;
            }
            return new Array(xTarg, yTarg);
        }
        this.moveInDirection = function(direction) {
            var xTarg, yTarg;
            var target = this.getTargetInDirection(direction);
            xTarg = target[0];
            yTarg = target[1];
            if(this.testTarget(xTarg, yTarg) == true) {
				if (spriteType=="wizard") {
					moveMonsters();
				}
                this.moveTo(xTarg, yTarg);
                return true;
            } else {
                return false;
            }
        }
        this.castSpell = function(spell) {
            // Here, get the power of the spell
            // Here, cast the spell
            var dir,target,touchedCells = '';
            for( dir = 1; dir <= 6; dir++) {
                target = this.getTargetInDirection(dir);
                touchedCells = touchedCells + ' ' + target[0] + '-' + target[1];
                var targetSelector = '.c-' + target[0] + '.l-' + target[1] ;
                $(targetSelector).html('&nbsp;').removeClass('clicked');
                var span = $('<span>' + plateau_jeu.resources.getResourceValue(spell[dir%3]) + '</span>');
                span.css('opacity',0);
                span.css('color', '#fff');
                $(targetSelector).append(span);
                $(targetSelector + ' span').animate({'opacity':1}, 400).animate({'opacity':0}, 800);
            }
            // The wizard repairs its own cell too. :)
            targetSelector = '.c-' + _x + '.l-' + _y ;
            $(targetSelector).html('&nbsp;').removeClass('clicked');
            // Loop on monsters. If they were touched, varnish them.
            var monster_id;
            for( monster_id = 0; monster_id < monsters.length; monster_id++ ) {
                var theMonster = monsters[monster_id];
                if(touchedCells.indexOf(theMonster.getX() + '-' + theMonster.getY()) > -1) {
                    theMonster.varnish();
                }
            }
        }
        this.getX = function() {
            return _x ;
        }
        this.getY = function() {
            return _y ;
        }
        this.varnish = function() {
            this.spriteTile.delay(500).animate({'opacity':0},1000,function() {
                $(this).removeClass('m-1').removeClass('m-2').removeClass('m-3').removeClass('m-4').removeClass('m-5');
                $(this).addClass('m-' + randomFromTo(1,5));
            });
            // TODO Here, gain score. Whee, monster killed!
            // In fact, he's not really dead. The creator of this evil minion doesn't know how to kill its JS object.
            this.moveTo(
                randomFromTo(2,settings.dimensions_plat_larg - 2),
                randomFromTo(2,settings.dimensions_plat_haut - 2)
            );
            this.spriteTile.delay(500).animate({'opacity':1},500);
        }
	this.addToMap();
	if (type=="wizard") {
		this.moveTo(6,6);
	} else if (type=="monster") {
		var m_x=Math.floor(Math.random()*(settings.dimensions_plat_larg-1));
		var m_y = Math.floor(Math.random()*(settings.dimensions_plat_haut-1));
		var delta_x_m=0;
		if ((m_y % 2 == 1)&&(m_x>0)) {
			delta_x_m = -1;
		}
		this.moveTo(m_x+delta_x_m,m_y);
		monsters.push(this);
	}
}
$(document).ready(function() {
	function Plat( options ) {
		settings = {
			'filename'	: 	'',
			'dimensions_plat_larg'	:	16,
			'dimensions_plat_haut'	:	16,
			'dimensions_hex_larg'	:	42,
			'dimensions_hex_haut'	:	48,
			'container'				:	'#plat',
			'hex_types_number'		:	6,
			'density'				:	0.15,
			'kind_of_resources'		:	6
		};
		plateau = $(settings.container);
		plateau.addClass('exploring');
		if ( options ) { 
				$.extend( settings, options );
			  }
		this.terrain = terrainGenerator( settings.dimensions_plat_larg, settings.dimensions_plat_haut );
		this.resources = generateMemory( settings.dimensions_plat_larg, settings.dimensions_plat_haut, plateau, 0, settings.kind_of_resources );
		this.spells = generateSpellTable(this.resources);
		this.init = function() {
			// Generate terrain.
			for(i = 0; i<settings.dimensions_plat_haut; i++) {
				var line = $('<div></div>');
				line.addClass(((i % 2)==1 ? 'line_i' : 'line_p'));
				line.addClass((i==0 ? '' : 'line_hex'));
				line.css('top',-1*i*(settings.dimensions_hex_haut*0.25));
				var nb_colonnes = settings.dimensions_plat_larg - (i % 2);
				for(j=0;j<nb_colonnes;j++) {
					var hex_type = this.terrain.getTerrain(j,i );
					var hex=$('<div>&nbsp;</div>');
					hex.addClass('hex_s');
					hex.addClass('c-'+j);
					hex.addClass('l-'+i);
					if (hex_type>0) {
						hex.addClass('hex_'+hex_type);
					}
					hex.hover(this.resources.viewResource,this.resources.hideResource);
					hex.click(this.resources.clickResource);
					line.append(hex);
				}
				plateau.append(line);
			}
			// Generate resource counter
			var listOfResources = $('<ul></ul>');
			for(i = 0; i < settings.kind_of_resources; i++) {
				var element = $('<li><small>' + (i+1) + '</small><b>' + this.resources.getResourceValue(i) + '</b>&nbsp;&times;&nbsp;<span>0</span></li>');
				element.attr('id','count-' + i);
				listOfResources.append(element);
			}
			$('#resource-count').append(listOfResources);
		}
		regenerateTerrain(); // launch terrain regenerator
		$(document).keydown(function(e) {
                        e.preventDefault();
			switch(e.keyCode) {
			//Wizard moves
				case 84: //T key
					wiz.moveInDirection(1);
					break;
				case 89: //Y key
					wiz.moveInDirection(2);
					break;
				case 72: //H key
					wiz.moveInDirection(3);
					break;
				case 66: //B key
					wiz.moveInDirection(4);
					break;
				case 86: //V key
					wiz.moveInDirection(5);
					break;
				case 70: //B key
					wiz.moveInDirection(6);
					break;
				
			//Spells
                                case 49:
				case 97:
					plateau_jeu.spells.addMana(0); // key 1, but mana type 0
					break;
				case 50:
				case 98:
					plateau_jeu.spells.addMana(1);
					break;
				case 51:
				case 99:
					plateau_jeu.spells.addMana(2);
					break;
				case 52:
				case 100:
					plateau_jeu.spells.addMana(3);
					break;
				case 53:
				case 101:
					plateau_jeu.spells.addMana(4);
					break;
				case 54:
				case 102:
					plateau_jeu.spells.addMana(5);
					break;
			}
			// 49-54 : 1 à 6 sur le clavier (%ù$*€ de raccourci sur le 4)
			// 97-102 : 1 à 6 pavé numérique
		});
	}
	plateau_jeu = new Plat();
	plateau_jeu.init();
	wiz = new Sprite("wizard");
	var monster_1 = new Sprite("monster");
	var monster_2 = new Sprite("monster");
        var monster_3 = new Sprite('monster');
        var monster_4 = new Sprite('monster');
        var monster_5 = new Sprite('monster');
});